package com.github.data_manage.controller

import com.github.data_manage.common.CommonPage
import com.github.data_manage.common.CommonResult
import com.github.data_manage.config.FUND_INTRODUCE_DIR
import com.github.data_manage.config.FUND_MONTH_REPORT_DIR
import com.github.data_manage.config.FUND_RESEARCH_DIR
import com.github.data_manage.config.FUND_TEMP_DIR
import com.github.data_manage.dao.FundIntroduceFileDao
import com.github.data_manage.dao.FundMonthReportFileDao
import com.github.data_manage.dao.FundResearchFileDao
import com.github.data_manage.domain.po.FundIntroduceFile
import com.github.data_manage.domain.po.FundMonthReportFile
import com.github.data_manage.domain.po.FundResearchFile
import com.github.data_manage.domain.vo.FundFilePageParam
import com.github.data_manage.enum.FundFileType
import com.github.data_manage.service.FundAiUploadService
import io.swagger.v3.oas.annotations.Operation
import io.swagger.v3.oas.annotations.tags.Tag
import jakarta.persistence.criteria.Predicate
import jakarta.servlet.http.HttpServletResponse
import org.springframework.data.domain.PageRequest
import org.springframework.data.domain.Sort
import org.springframework.data.jpa.domain.Specification
import org.springframework.http.HttpHeaders
import org.springframework.http.MediaType
import org.springframework.util.StringUtils
import org.springframework.web.bind.annotation.*
import org.springframework.web.multipart.MultipartFile
import java.io.File
import java.io.FileOutputStream
import java.net.URLEncoder
import java.nio.charset.StandardCharsets
import java.nio.file.Files
import java.nio.file.Paths
import java.time.LocalDateTime

@RestController
@Tag(name = "FundFilesController", description = "基金附件管理")
@RequestMapping("/fundFiles")
class FundFileController(
    private val fundIntroduceFileDao: FundIntroduceFileDao,
    private val fundMonthReportFileDao: FundMonthReportFileDao,
    private val fundResearchFileDao: FundResearchFileDao,
    private val fundAiUploadService: FundAiUploadService
) {

    fun saveFile(
        file: File,
        fundId: Int,
        fundFileType: FundFileType
    ) {
        val baseDir = when (fundFileType) {
            FundFileType.INTRODUCE -> FUND_INTRODUCE_DIR
            FundFileType.MONTH_REPORT -> FUND_MONTH_REPORT_DIR
            FundFileType.RESEARCH -> FUND_RESEARCH_DIR
        }
        if (!Files.exists(Paths.get("$baseDir/${fundId}"))) {
            Files.createDirectory(Paths.get("$baseDir/${fundId}"))
        }
        val targetFileName = appendTimeStamp(file.name)
        val targetFilePath = Paths.get("$baseDir/${fundId}/$targetFileName")
        if (Files.exists(targetFilePath)) {
            return
        }
        Files.copy(file.toPath(), targetFilePath)
        when (fundFileType) {
            FundFileType.INTRODUCE -> {
                val fundIntroduceFile = FundIntroduceFile()
                fundIntroduceFile.fundId = fundId
                fundIntroduceFile.fileName = targetFileName
                fundIntroduceFile.createTime = LocalDateTime.now()
                fundIntroduceFileDao.save(fundIntroduceFile)
            }

            FundFileType.MONTH_REPORT -> {
                val fundMonthReportFile = FundMonthReportFile()
                fundMonthReportFile.fundId = fundId
                fundMonthReportFile.fileName = targetFileName
                fundMonthReportFile.createTime = LocalDateTime.now()
                fundMonthReportFileDao.save(fundMonthReportFile)
            }

            FundFileType.RESEARCH -> {
                val fundResearchFile = FundResearchFile()
                fundResearchFile.fundId = fundId
                fundResearchFile.fileName = targetFileName
                fundResearchFile.createTime = LocalDateTime.now()
                fundResearchFileDao.save(fundResearchFile)
            }
        }
    }


    @PostMapping("/introduce/upload")
    @Operation(description = "上传基金介绍文件")
    fun uploadFundIntroduceFile(
        @RequestParam("file") file: MultipartFile,
        @RequestParam("fundId") fundId: Int?
    ): CommonResult<Boolean> {
        val tempFile = File("$FUND_TEMP_DIR/${file.originalFilename}")
        FileOutputStream(tempFile).use { fos ->
            file.inputStream.use { fis ->
                fis.transferTo(fos)
            }
        }
        if (fundId != null) {
            saveFile(tempFile, fundId, FundFileType.INTRODUCE)
        }
        fundAiUploadService.uploadFundInfoFilesToAi(
            arrayOf(tempFile),
            mapOf(
                Pair(
                    tempFile.name, FundFileType.INTRODUCE
                )
            )
        )
        return CommonResult.success(true)
    }

    @PostMapping("/upload/batch")
    @Operation(description = "批量上传基金文件")
    fun batchUploadFundIntroduceFile(
        @RequestParam("introduces", required = false) introduces: Array<MultipartFile>?,
        @RequestParam("monthReports", required = false) monthReports: Array<MultipartFile>?,
        @RequestParam("fundId") fundId: Int?
    ): CommonResult<Boolean> {
        val tempFiles = mutableListOf<File>()
        val fileTypeMap = mutableMapOf<String, FundFileType>()
        introduces?.forEach { file ->
            val tempFile = File("$FUND_TEMP_DIR/${file.originalFilename}")
            FileOutputStream(tempFile).use { fos ->
                file.inputStream.use { fis ->
                    fis.transferTo(fos)
                }
            }
            if (fundId != null) {
                saveFile(tempFile, fundId, FundFileType.INTRODUCE)
            }
            tempFiles.add(tempFile)
            fileTypeMap[tempFile.name] = FundFileType.INTRODUCE
        }
        monthReports?.forEach { file ->
            val tempFile = File("$FUND_TEMP_DIR/${file.originalFilename}")
            FileOutputStream(tempFile).use { fos ->
                file.inputStream.use { fis ->
                    fis.transferTo(fos)
                }
            }
            if (fundId != null) {
                saveFile(tempFile, fundId, FundFileType.MONTH_REPORT)
            }
            tempFiles.add(tempFile)
            fileTypeMap[tempFile.name] = FundFileType.MONTH_REPORT
        }
        fundAiUploadService.uploadFundInfoFilesToAi(tempFiles.toTypedArray(), fileTypeMap)
        return CommonResult.success(true)
    }

    @PostMapping("/introduce/list")
    @Operation(description = "查询基金介绍文件")
    fun getFundIntroduceList(
        @RequestBody fundFilePageParam: FundFilePageParam
    ): CommonResult<CommonPage<FundIntroduceFile>> {
        val sort = if (StringUtils.hasText(fundFilePageParam.sortField)) {
            val direction = when (fundFilePageParam.sortOrder?.lowercase()) {
                "desc" -> Sort.Direction.DESC
                else -> Sort.Direction.ASC
            }
            Sort.by(direction, fundFilePageParam.sortField)
        } else {
            Sort.unsorted()
        }
        val page = PageRequest.of(fundFilePageParam.pageNum, fundFilePageParam.pageSize, sort)
//        val page =
//            PageUtil.buildJpaPage<FundIntroduceFile>(fundFilePageParam.pageNum, fundFilePageParam.pageSize)
        val condition = Specification<FundIntroduceFile> { root, _, builder ->
            val conditions = mutableListOf<Predicate>()
            conditions.add(builder.equal(root.get<Int>("fundId"), fundFilePageParam.fundId))
            if (StringUtils.hasText(fundFilePageParam.name)) {
                conditions.add(
                    builder.like(
                        root.get<String>("fileName"),
                        "%${fundFilePageParam.name}%"
                    )
                )

            }
            return@Specification builder.and(*conditions.toTypedArray())
        }
        return CommonResult.success(CommonPage.restPage(fundIntroduceFileDao.findAll(condition, page)))

    }

    @DeleteMapping("/introduce/delete")
    @Operation(description = "删除基金介绍文件")
    fun deleteFundIntroduceFile(
        @RequestParam id: Int
    ): CommonResult<Boolean> {
        fundIntroduceFileDao.findById(id).ifPresent {
            val filePath = Paths.get("${FUND_INTRODUCE_DIR}/${it.fundId}/${it.fileName}")
            Files.deleteIfExists(filePath)
            fundIntroduceFileDao.deleteById(id)
        }
        return CommonResult.success(true)
    }

    @GetMapping("/introduce/download")
    @Operation(description = "下载基金介绍文件")
    fun downloadFundIntroduceFile(
        @RequestParam id: Int,
        servletResponse: HttpServletResponse
    ) {
        fundIntroduceFileDao.findById(id).ifPresent {
            val filePath = Paths.get("${FUND_INTRODUCE_DIR}/${it.fundId}/${it.fileName}")
            if (Files.exists(filePath)) {
                servletResponse.contentType = MediaType.APPLICATION_OCTET_STREAM_VALUE
                servletResponse.setHeader(
                    HttpHeaders.CONTENT_DISPOSITION,
                    "attachment;filename=" + URLEncoder.encode(it.fileName, StandardCharsets.UTF_8)
                )
                Files.newInputStream(filePath).use { fis ->
                    servletResponse.outputStream.use { fos -> fis.copyTo(fos) }
                }
            }
        }
    }

    @GetMapping("/introduce/download/name")
    @Operation(description = "根据文件名下载基金介绍文件")
    fun downloadFundIntroduceFileByName(
        @RequestParam fundId: Int,
        @RequestParam name: String,
        servletResponse: HttpServletResponse
    ) {
        val filePath = Paths.get("${FUND_INTRODUCE_DIR}/${fundId}/${name}")
        if (Files.exists(filePath)) {
            servletResponse.contentType = MediaType.APPLICATION_OCTET_STREAM_VALUE
            servletResponse.setHeader(
                HttpHeaders.CONTENT_DISPOSITION,
                "attachment;filename=" + URLEncoder.encode(name, StandardCharsets.UTF_8)
            )
            Files.newInputStream(filePath).use { fis ->
                servletResponse.outputStream.use { fos -> fis.copyTo(fos) }
            }
        }
    }


    @PostMapping("/monthReport/upload")
    @Operation(description = "上传基金月报文件")
    fun uploadFundMonthReportFile(
        @RequestParam("file") file: MultipartFile,
        @RequestParam("fundId") fundId: Int?
    ): CommonResult<Boolean> {
//        val fundDir = Paths.get("${FUND_MONTH_REPORT_DIR}/${fundId}")
//        if (!Files.exists(fundDir)) {
//            Files.createDirectory(fundDir)
//        }
//        val targetFileName = appendTimeStamp(file.originalFilename!!)
//
//        val filePath = Paths.get("$fundDir/${targetFileName}")
//        Files.deleteIfExists(filePath)
//        Files.newOutputStream(filePath).use { fos ->
//            file.inputStream.use { fis -> fis.copyTo(fos) }
//        }
        val tempFile = File("$FUND_TEMP_DIR/${file.originalFilename}")
        FileOutputStream(tempFile).use { fos ->
            file.inputStream.use { fis ->
                fis.transferTo(fos)
            }
        }
        if (fundId != null) {
            saveFile(tempFile, fundId, FundFileType.MONTH_REPORT)
        }
        fundAiUploadService.uploadFundInfoFilesToAi(arrayOf(tempFile), mapOf(
            Pair(
                tempFile.name, FundFileType.MONTH_REPORT
            )
        ))
        return CommonResult.success(true)
    }

//    @PostMapping("/monthReport/upload/batch")
//    @Operation(description = "批量上传基月报文件")
//    fun batchUploadFundMonthReportFile(
//        @RequestParam("files") files: Array<MultipartFile>,
//        @RequestParam("fundId") fundId: Int?
//    ): CommonResult<Boolean> {
//        val tempFiles = mutableListOf<File>()
//        for (file in files) {
//            val tempFile = File("$FUND_TEMP_DIR/${file.originalFilename}")
//            FileOutputStream(tempFile).use { fos ->
//                file.inputStream.use { fis ->
//                    fis.transferTo(fos)
//                }
//            }
//            if (fundId != null) {
//                saveFile(tempFile, fundId, FundFileType.MONTH_REPORT)
//            }
//            tempFiles.add(tempFile)
//        }
//        fundAiUploadService.uploadFundInfoFilesToAi(tempFiles.toTypedArray(), FundFileType.MONTH_REPORT)
//        return CommonResult.success(true)
//    }

    @PostMapping("/monthReport/list")
    @Operation(description = "查询基金月报文件")
    fun getFundMonthReportList(
        @RequestBody fundFilePageParam: FundFilePageParam
    ): CommonResult<CommonPage<FundMonthReportFile>> {
        val sort = if (StringUtils.hasText(fundFilePageParam.sortField)) {
            val direction = when (fundFilePageParam.sortOrder?.lowercase()) {
                "desc" -> Sort.Direction.DESC
                else -> Sort.Direction.ASC
            }
            Sort.by(direction, fundFilePageParam.sortField)
        } else {
            Sort.unsorted()
        }
        val page = PageRequest.of(fundFilePageParam.pageNum, fundFilePageParam.pageSize, sort)
        val condition = Specification<FundMonthReportFile> { root, _, builder ->
            val conditions = mutableListOf<Predicate>()
            conditions.add(builder.equal(root.get<Int>("fundId"), fundFilePageParam.fundId))
            if (StringUtils.hasText(fundFilePageParam.name)) {
                conditions.add(
                    builder.like(
                        root.get<String>("fileName"),
                        "%${fundFilePageParam.name}%"
                    )
                )

            }
            return@Specification builder.and(*conditions.toTypedArray())
        }
        return CommonResult.success(CommonPage.restPage(fundMonthReportFileDao.findAll(condition, page)))

    }

    @DeleteMapping("/monthReport/delete")
    @Operation(description = "删除基金月报文件")
    fun deleteFundMonthReportFile(
        @RequestParam id: Int
    ): CommonResult<Boolean> {
        fundMonthReportFileDao.findById(id).ifPresent {
            val filePath = Paths.get("${FUND_MONTH_REPORT_DIR}/${it.fundId}/${it.fileName}")
            Files.deleteIfExists(filePath)
            fundMonthReportFileDao.deleteById(id)
        }
        return CommonResult.success(true)
    }

    @GetMapping("/monthReport/download")
    @Operation(description = "下载基金月报文件")
    fun downloadFundMonthReportFile(
        @RequestParam id: Int,
        servletResponse: HttpServletResponse
    ) {
        fundMonthReportFileDao.findById(id).ifPresent {
            val filePath = Paths.get("${FUND_MONTH_REPORT_DIR}/${it.fundId}/${it.fileName}")
            if (Files.exists(filePath)) {
                servletResponse.contentType = MediaType.APPLICATION_OCTET_STREAM_VALUE
                servletResponse.setHeader(
                    HttpHeaders.CONTENT_DISPOSITION,
                    "attachment;filename=" + URLEncoder.encode(it.fileName, StandardCharsets.UTF_8)
                )
                Files.newInputStream(filePath).use { fis ->
                    servletResponse.outputStream.use { fos -> fis.copyTo(fos) }
                }
            }
        }
    }

    @GetMapping("/monthReport/download/name")
    @Operation(description = "根据文件名下载基金月报文件")
    fun downloadFundMonthReportFileByName(
        @RequestParam fundId: Int,
        @RequestParam name: String,
        servletResponse: HttpServletResponse
    ) {
        val filePath = Paths.get("${FUND_MONTH_REPORT_DIR}/${fundId}/${name}")
        if (Files.exists(filePath)) {
            servletResponse.contentType = MediaType.APPLICATION_OCTET_STREAM_VALUE
            servletResponse.setHeader(
                HttpHeaders.CONTENT_DISPOSITION,
                "attachment;filename=" + URLEncoder.encode(name, StandardCharsets.UTF_8)
            )
            Files.newInputStream(filePath).use { fis ->
                servletResponse.outputStream.use { fos -> fis.copyTo(fos) }
            }
        }
    }

    private fun appendTimeStamp(fileName: String): String =
        "${fileName.substringBeforeLast(".")}_${System.currentTimeMillis()}.${fileName.substringAfterLast(".")}"


    @PostMapping("/research/build")
    @Operation(description = "ai 生成基金尽调文件")
    fun buildFundResearchFile(@RequestParam("fundId") fundId: Int): CommonResult<Boolean> {
        return CommonResult.success(true)
    }

    @PostMapping("/research/upload")
    @Operation(description = "上传基金尽调文件")
    fun uploadFundResearchFile(
        @RequestParam("file") file: MultipartFile,
        @RequestParam("fundId") fundId: Int
    ): CommonResult<Boolean> {
//        val fundDir = Paths.get("${FUND_RESEARCH_DIR}/${fundId}")
//        if (!Files.exists(fundDir)) {
//            Files.createDirectory(fundDir)
//        }
//        val targetFileName = appendTimeStamp(file.originalFilename!!)
//        val filePath = Paths.get("$fundDir/${targetFileName}")
//        Files.deleteIfExists(filePath)
//        Files.newOutputStream(filePath).use { fos ->
//            file.inputStream.use { fis -> fis.copyTo(fos) }
//        }
        val tempFile = File("$FUND_TEMP_DIR/${file.originalFilename}")
        FileOutputStream(tempFile).use { fos ->
            file.inputStream.use { fis ->
                fis.transferTo(fos)
            }
        }
//        if (fundId != null) {
        saveFile(tempFile, fundId, FundFileType.RESEARCH)
        tempFile.delete()
//        }
//        fundAiUploadService.uploadFundInfoFileToAi(tempFile, FundFileType.RESEARCH)
        return CommonResult.success(true)
    }

    @PostMapping("/research/upload/batch")
    @Operation(description = "批量上传基尽调文件")
    fun batchUploadFundResearchFile(
        @RequestParam("files") files: Array<MultipartFile>,
        @RequestParam("fundId") fundId: Int
    ): CommonResult<Boolean> {
        for (file in files) {
            val tempFile = File("$FUND_TEMP_DIR/${file.originalFilename}")
            FileOutputStream(tempFile).use { fos ->
                file.inputStream.use { fis ->
                    fis.transferTo(fos)
                }
            }
            saveFile(tempFile, fundId, FundFileType.RESEARCH)
        }
        return CommonResult.success(true)
    }

    @PostMapping("/research/list")
    @Operation(description = "查询基金尽调文件")
    fun getFundResearchList(
        @RequestBody fundFilePageParam: FundFilePageParam
    ): CommonResult<CommonPage<FundResearchFile>> {
        val sort = if (StringUtils.hasText(fundFilePageParam.sortField)) {
            val direction = when (fundFilePageParam.sortOrder?.lowercase()) {
                "desc" -> Sort.Direction.DESC
                else -> Sort.Direction.ASC
            }
            Sort.by(direction, fundFilePageParam.sortField)
        } else {
            Sort.unsorted()
        }
        val page = PageRequest.of(fundFilePageParam.pageNum, fundFilePageParam.pageSize, sort)
        val condition = Specification<FundResearchFile> { root, _, builder ->
            val conditions = mutableListOf<Predicate>()
            conditions.add(builder.equal(root.get<Int>("fundId"), fundFilePageParam.fundId))
            if (StringUtils.hasText(fundFilePageParam.name)) {
                conditions.add(
                    builder.like(
                        root.get<String>("fileName"),
                        "%${fundFilePageParam.name}%"
                    )
                )
            }
            return@Specification builder.and(*conditions.toTypedArray())
        }
        return CommonResult.success(CommonPage.restPage(fundResearchFileDao.findAll(condition, page)))

    }

    @DeleteMapping("/research/delete")
    @Operation(description = "删除基金尽调文件")
    fun deleteFundResearchFile(
        @RequestParam id: Int
    ): CommonResult<Boolean> {
        fundResearchFileDao.findById(id).ifPresent {
            val filePath = Paths.get("${FUND_RESEARCH_DIR}/${it.fundId}/${it.fileName}")
            Files.deleteIfExists(filePath)
            fundResearchFileDao.deleteById(id)
        }
        return CommonResult.success(true)
    }

    @GetMapping("/research/download")
    @Operation(description = "下载基金尽调文件")
    fun downloadFundResearchFile(
        @RequestParam id: Int,
        servletResponse: HttpServletResponse
    ) {
        fundResearchFileDao.findById(id).ifPresent {
            val filePath = Paths.get("${FUND_RESEARCH_DIR}/${it.fundId}/${it.fileName}")
            if (Files.exists(filePath)) {
                servletResponse.contentType = MediaType.APPLICATION_OCTET_STREAM_VALUE
                servletResponse.setHeader(
                    HttpHeaders.CONTENT_DISPOSITION,
                    "attachment;filename=" + URLEncoder.encode(it.fileName, StandardCharsets.UTF_8)
                )
                Files.newInputStream(filePath).use { fis ->
                    servletResponse.outputStream.use { fos -> fis.copyTo(fos) }
                }
            }
        }
    }
}
